//package org.apache.dubbo.config.spring.beans.factory.annotation;
//
//import lombok.extern.slf4j.Slf4j;
//import org.apache.dubbo.common.utils.ArrayUtils;
//import org.apache.dubbo.config.MethodConfig;
//import org.apache.dubbo.config.annotation.DubboService;
//import org.apache.dubbo.config.annotation.Method;
//import org.apache.dubbo.config.annotation.Service;
//import org.apache.dubbo.config.spring.ServiceBean;
//import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener;
//import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner;
//import org.springframework.beans.BeansException;
//import org.springframework.beans.MutablePropertyValues;
//import org.springframework.beans.factory.BeanClassLoaderAware;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.config.*;
//import org.springframework.beans.factory.support.*;
//import org.springframework.cloud.openfeign.FeignClient;
//import org.springframework.context.EnvironmentAware;
//import org.springframework.context.ResourceLoaderAware;
//import org.springframework.context.annotation.AnnotationBeanNameGenerator;
//import org.springframework.context.annotation.AnnotationConfigUtils;
//import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
//import org.springframework.context.annotation.ConfigurationClassPostProcessor;
//import org.springframework.core.annotation.AnnotationAttributes;
//import org.springframework.core.env.Environment;
//import org.springframework.core.io.ResourceLoader;
//import org.springframework.core.type.filter.AnnotationTypeFilter;
//import org.springframework.util.*;
//
//import java.lang.annotation.Annotation;
//import java.util.*;
//
//import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
//import static com.alibaba.spring.util.BeanRegistrar.registerInfrastructureBean;
//import static com.alibaba.spring.util.ObjectUtils.of;
//import static java.util.Arrays.asList;
//import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create;
//import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
//import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
//import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
//import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes;
//import static org.springframework.util.ClassUtils.getAllInterfacesForClass;
//import static org.springframework.util.ClassUtils.resolveClassName;
//import static org.springframework.util.StringUtils.hasText;
//
//
//@Slf4j
//public class DubboFeignProviderBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
//        ResourceLoaderAware, BeanClassLoaderAware {
//
//    private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
//            // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
//            DubboService.class,
//            // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
//            Service.class,
//            // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
//            com.alibaba.dubbo.config.annotation.Service.class
//    );
//
//    private static final String SEPARATOR = ":";
//
//    private final Set<String> packagesToScan;
//
//    private Environment environment;
//
//    private ResourceLoader resourceLoader;
//
//    private ClassLoader classLoader;
//
//    @Autowired
//    private final DubboService defaultService;
//
//    public DubboFeignProviderBeanPostProcessor(String... packagesToScan) {
//        this(Arrays.asList(packagesToScan));
//    }
//
//    public DubboFeignProviderBeanPostProcessor(Collection<String> packagesToScan) {
//        this(new LinkedHashSet<String>(packagesToScan));
//    }
//
//    public DubboFeignProviderBeanPostProcessor(Set<String> packagesToScan) {
//        this.packagesToScan = packagesToScan;
//        @DubboService
//        final class DefaultServiceClass {
//        }
//        ;
//        this.defaultService = DefaultServiceClass.class.getAnnotation(DubboService.class);
//    }
//
//    @Override
//    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//
//        // @since 2.7.5
//        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
//
//        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
//
//        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
//            registerServiceBeans(resolvedPackagesToScan, registry);
//        } else {
//            log.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
//        }
//    }
//
//    /**
//     * Registers Beans whose classes was annotated {@link FeignClient}
//     *
//     * @param packagesToScan The base packages to scan
//     * @param registry       {@link BeanDefinitionRegistry}
//     */
//    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
//
//        DubboClassPathBeanDefinitionScanner scanner =
//                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
//
//        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
//
//        scanner.setBeanNameGenerator(beanNameGenerator);
//
//        // refactor @since 2.7.7
//        serviceAnnotationTypes.forEach(annotationType -> {
//            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
//        });
//
//        for (String packageToScan : packagesToScan) {
//
//            // Registers @DubboService Bean first
//            scanner.scan(packageToScan);
//
//            // Finds all BeanDefinitionHolders of @DubboService whether @ComponentScan scans or not.
//            Set<BeanDefinitionHolder> beanDefinitionHolders =
//                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
//
//            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
//
//                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
//                    registerServiceBean(beanDefinitionHolder, registry, scanner);
//                }
//                log.info(beanDefinitionHolders.size() + " annotated Dubbo's @DubboService Components { " +
//                        beanDefinitionHolders +
//                        " } were scanned under package[" + packageToScan + "]");
//            } else {
//                log.warn("No Spring Bean annotating Dubbo's @DubboService was found under package["
//                        + packageToScan + "]");
//            }
//
//        }
//
//    }
//
//    /**
//     * It'd better to use BeanNameGenerator instance that should reference
//     * {@link ConfigurationClassPostProcessor},
//     * thus it maybe a potential problem on bean name generation.
//     *
//     * @param registry {@link BeanDefinitionRegistry}
//     * @return {@link BeanNameGenerator} instance
//     * @see SingletonBeanRegistry
//     * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
//     * @see ConfigurationClassPostProcessor#processConfigBeanDefinitions
//     * @since 2.5.8
//     */
//    private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) {
//
//        BeanNameGenerator beanNameGenerator = null;
//
//        if (registry instanceof SingletonBeanRegistry) {
//            SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry);
//            beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
//        }
//
//        if (beanNameGenerator == null) {
//            log.info("BeanNameGenerator bean can't be found in BeanFactory with name ["
//                    + CONFIGURATION_BEAN_NAME_GENERATOR + "]");
//            log.info("BeanNameGenerator will be a instance of " +
//                    AnnotationBeanNameGenerator.class.getName() +
//                    " , it maybe a potential problem on bean name generation.");
//            beanNameGenerator = new AnnotationBeanNameGenerator();
//        }
//        return beanNameGenerator;
//    }
//    /**
//     * Finds a {@link Set} of {@link BeanDefinitionHolder BeanDefinitionHolders} whose bean type annotated
//     * {@link DubboService} Annotation.
//     *
//     * @param scanner       {@link ClassPathBeanDefinitionScanner}
//     * @param packageToScan pachage to scan
//     * @param registry      {@link BeanDefinitionRegistry}
//     * @return non-null
//     * @since 2.5.8
//     */
//    private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
//            ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
//            BeanNameGenerator beanNameGenerator) {
//
//        Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);
//
//        Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<BeanDefinitionHolder>(beanDefinitions.size());
//
//        for (BeanDefinition beanDefinition : beanDefinitions) {
//
//            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
//            BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
//            beanDefinitionHolders.add(beanDefinitionHolder);
//
//        }
//        return beanDefinitionHolders;
//
//    }
//
//    /**
//     * Registers {@link ServiceBean} from new annotated {@link DubboService} {@link BeanDefinition}
//     *
//     * @param beanDefinitionHolder
//     * @param registry
//     * @param scanner
//     * @see ServiceBean
//     * @see BeanDefinition
//     */
//    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
//                                     DubboClassPathBeanDefinitionScanner scanner) {
//
//        Class<?> beanClass = resolveClass(beanDefinitionHolder);
//
//        Annotation service = findServiceAnnotation(beanClass);
//
//        /**
//         * The {@link AnnotationAttributes} of @Service annotation
//         */
//        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
//
//        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
//
//        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
//
//        AbstractBeanDefinition serviceBeanDefinition =
//                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
//
//        // ServiceBean Bean name
//        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
//
//        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
//            registry.registerBeanDefinition(beanName, serviceBeanDefinition);
//            log.warn("The BeanDefinition[" + serviceBeanDefinition +
//                    "] of ServiceBean has been registered with name : " + beanName);
//        } else {
//            log.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
//                    "] of ServiceBean[ bean name : " + beanName +
//                    "] was be found , Did @DubboComponentScan scan to same package in many times?");
//        }
//    }
//
//    /**
//     * Generates the bean name of {@link ServiceBean}
//     *
//     * @param serviceAnnotationAttributes
//     * @param interfaceClass              the class of interface annotated {@link Service}
//     * @return ServiceBean@interfaceClassName#annotatedServiceBeanName
//     * @since 2.7.3
//     */
//    private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
//        ServiceBeanNameBuilder builder = create(interfaceClass, environment)
//                .group(serviceAnnotationAttributes.getString("group"))
//                .version(serviceAnnotationAttributes.getString("version"));
//        return builder.build();
//    }
//
//    private Class<?> resolveServiceInterfaceClass(AnnotationAttributes attributes, Class<?> defaultInterfaceClass)
//            throws IllegalArgumentException {
//
//        ClassLoader classLoader = defaultInterfaceClass != null ? defaultInterfaceClass.getClassLoader() : Thread.currentThread().getContextClassLoader();
//        Class<?> interfaceClass = getAttribute(attributes, "interfaceClass");
//
//        if (void.class.equals(interfaceClass)) {
//
//            interfaceClass = null;
//
//            String interfaceClassName = getAttribute(attributes, "interfaceName");
//
//            if (hasText(interfaceClassName)) {
//                if (ClassUtils.isPresent(interfaceClassName, classLoader)) {
//                    interfaceClass = resolveClassName(interfaceClassName, classLoader);
//                }
//            }
//        }
//
//        if (interfaceClass == null && defaultInterfaceClass != null) {
//            // Find all interfaces from the annotated class
//            // To resolve an issue : https://github.com/apache/dubbo/issues/3251
//            Class<?>[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass);
//
//            if (allInterfaces.length > 0) {
//                interfaceClass = allInterfaces[0];
//            }
//
//        }
//        Assert.notNull(interfaceClass,
//                "@Service interfaceClass() or interfaceName() or interface class must be present!");
//
//        Assert.isTrue(interfaceClass.isInterface(),
//                "The annotated type must be an interface!");
//
//        return interfaceClass;
//    }
//
//    private Class<?> resolveClass(BeanDefinitionHolder beanDefinitionHolder) {
//
//        BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
//        return resolveClass(beanDefinition);
//
//    }
//
//    private Class<?> resolveClass(BeanDefinition beanDefinition) {
//
//        String beanClassName = beanDefinition.getBeanClassName();
//        return resolveClassName(beanClassName, classLoader);
//
//    }
//
//
//    /**
//     * Find the {@link Annotation annotation} of @Service
//     *
//     * @param beanClass the {@link Class class} of Bean
//     * @return <code>null</code> if not found
//     * @since 2.7.3
//     */
//    private Annotation findServiceAnnotation(Class<?> beanClass) {
//        return serviceAnnotationTypes
//                .stream()
//                .map(annotationType -> findMergedAnnotation(beanClass, annotationType))
//                .filter(Objects::nonNull)
//                .findFirst()
//                .orElse(null);
//    }
//
//    private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
//        Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
//        for (String packageToScan : packagesToScan) {
//            if (StringUtils.hasText(packageToScan)) {
//                String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
//                resolvedPackagesToScan.add(resolvedPackageToScan);
//            }
//        }
//        return resolvedPackagesToScan;
//    }
//
//    /**
//     * Build the {@link AbstractBeanDefinition Bean Definition}
//     *
//     * @param serviceAnnotation
//     * @param serviceAnnotationAttributes
//     * @param interfaceClass
//     * @param annotatedServiceBeanName
//     * @return
//     * @since 2.7.3
//     */
//    private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
//                                                              AnnotationAttributes serviceAnnotationAttributes,
//                                                              Class<?> interfaceClass,
//                                                              String annotatedServiceBeanName) {
//
//        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
//
//        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
//
//        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
//
//        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
//                "interface", "interfaceName", "parameters");
//
//        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
//
//        // References "ref" property to annotated-@Service Bean
//        addPropertyReference(builder, "ref", annotatedServiceBeanName);
//        // Set interface
//        builder.addPropertyValue("interface", interfaceClass.getName());
//        // Convert parameters into map
//        builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
//        // Add methods parameters
//        List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
//        if (!methodConfigs.isEmpty()) {
//            builder.addPropertyValue("methods", methodConfigs);
//        }
//
//        /**
//         * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
//         */
//        String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
//        if (StringUtils.hasText(providerConfigBeanName)) {
//            addPropertyReference(builder, "provider", providerConfigBeanName);
//        }
//
//        /**
//         * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
//         */
//        String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
//        if (StringUtils.hasText(monitorConfigBeanName)) {
//            addPropertyReference(builder, "monitor", monitorConfigBeanName);
//        }
//
//        /**
//         * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
//         */
//        String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
//        if (StringUtils.hasText(applicationConfigBeanName)) {
//            addPropertyReference(builder, "application", applicationConfigBeanName);
//        }
//
//        /**
//         * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
//         */
//        String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
//        if (StringUtils.hasText(moduleConfigBeanName)) {
//            addPropertyReference(builder, "module", moduleConfigBeanName);
//        }
//
//
//        /**
//         * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
//         */
//        String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
//
//        List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
//
//        if (!registryRuntimeBeanReferences.isEmpty()) {
//            builder.addPropertyValue("registries", registryRuntimeBeanReferences);
//        }
//
//        /**
//         * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
//         */
//        String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
//
//        List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
//
//        if (!protocolRuntimeBeanReferences.isEmpty()) {
//            builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
//        }
//
//        return builder.getBeanDefinition();
//
//    }
//
//
//    private ManagedList<RuntimeBeanReference> toRuntimeBeanReferences(String... beanNames) {
//
//        ManagedList<RuntimeBeanReference> runtimeBeanReferences = new ManagedList<RuntimeBeanReference>();
//
//        if (!ObjectUtils.isEmpty(beanNames)) {
//
//            for (String beanName : beanNames) {
//
//                String resolvedBeanName = environment.resolvePlaceholders(beanName);
//
//                runtimeBeanReferences.add(new RuntimeBeanReference(resolvedBeanName));
//            }
//
//        }
//
//        return runtimeBeanReferences;
//
//    }
//
//    private void addPropertyReference(BeanDefinitionBuilder builder, String propertyName, String beanName) {
//        String resolvedBeanName = environment.resolvePlaceholders(beanName);
//        builder.addPropertyReference(propertyName, resolvedBeanName);
//    }
//
//
//    @Override
//    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//
//    }
//
//    @Override
//    public void setEnvironment(Environment environment) {
//        this.environment = environment;
//    }
//
//    @Override
//    public void setResourceLoader(ResourceLoader resourceLoader) {
//        this.resourceLoader = resourceLoader;
//    }
//
//    @Override
//    public void setBeanClassLoader(ClassLoader classLoader) {
//        this.classLoader = classLoader;
//    }
//
//    private List convertMethodConfigs(Object methodsAnnotation) {
//        if (methodsAnnotation == null) {
//            return Collections.EMPTY_LIST;
//        }
//        return MethodConfig.constructMethodConfig((Method[]) methodsAnnotation);
//    }
//
//    private Map<String, String> convertParameters(String[] parameters) {
//        if (ArrayUtils.isEmpty(parameters)) {
//            return null;
//        }
//
//        if (parameters.length % 2 != 0) {
//            throw new IllegalArgumentException("parameter attribute must be paired with key followed by value");
//        }
//
//        Map<String, String> map = new HashMap<>();
//        for (int i = 0; i < parameters.length; i += 2) {
//            map.put(parameters[i], parameters[i + 1]);
//        }
//        return map;
//    }
//}
